package cshi.network;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Set;

public class Nio_TimeServer {
	public static void main(String[] args) throws IOException {
		int port = 8090;
		if (args != null && args.length > 0){
			try {
				port = Integer.valueOf(args[0]);
			} catch (NumberFormatException e) {
				// TODO: handle exception
			}
		}
		MultiplexerTimeServerHandler timeServer = new MultiplexerTimeServerHandler(port);
		new Thread(timeServer,"nio-timeserver").start();
	}
}

class MultiplexerTimeServerHandler implements Runnable {
	private Selector sel;//selector�͹�����̴߳���
	private ServerSocketChannel ssc;
	private volatile boolean stop;
	
	public MultiplexerTimeServerHandler(int port){
		try {
			sel = Selector.open();
			
			ssc = ServerSocketChannel.open();
			ssc.configureBlocking(false);//non-block channel
			ssc.socket().bind(new InetSocketAddress(port), 1024);//1024 is backlog
			ssc.register(sel, SelectionKey.OP_ACCEPT);
			
			System.out.println("time server started at port :" + port);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
			System.exit(-1);
		}
	}
	
	public void stop() {
		this.stop = true;
	}
	public void run() {
		while (!stop) {
			try {
				sel.select(1000);//do select op every 1s
				Set<SelectionKey> selectedKeys = sel.selectedKeys();
				
				java.util.Iterator<SelectionKey> it = selectedKeys.iterator();
				
				SelectionKey key = null;
				while (it.hasNext()){
					key = it.next();
					it.remove();
					try {
						process(key);
					} catch (Exception e) {
						// TODO: handle exception
						if (key != null){
							key.cancel();
							if (key.channel() != null)
								//Returns the channel for which this key was created
								key.channel().close();
						}
					}
				}
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
		if (sel != null) {
			try {
				sel.close();
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
	}
	
	private void process(SelectionKey key) throws IOException {
		if (key.isValid()){
			//Tests whether this key's channel 
			//is ready to accept a new socket connection
			
			if (key.isAcceptable()){
				ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
				//Accepts a connection made to this channel's socket
				SocketChannel sc = ssc.accept();
				sc.configureBlocking(false);
				
				sc.register(sel, SelectionKey.OP_READ);
				System.out.println("accept key coming");
			}
			if (key.isReadable()){
				SocketChannel sc = (SocketChannel)key.channel();
				ByteBuffer bb = ByteBuffer.allocate(1024);
				int readBytes = sc.read(bb);
				if (readBytes > 0){
					bb.flip();
					byte[] bytes = new byte[bb.remaining()];
					bb.get(bytes);//transfer data from buffer to array
					String body = new String(bytes, "UTF-8");
					System.out.println("the time server receive command:" + body);
					String curTime = "GET TIME".equalsIgnoreCase(body) ? 
							new Date(System.currentTimeMillis()).toString() :
								"BAD COMMAND";
					doWrite(sc,curTime);
				}
				else if (readBytes < 0) {
					key.cancel();
					sc.close();
				}else {
					; // 0 byte
				}
			}
		}
	}
	
	private void doWrite(SocketChannel channel,String resp) throws IOException{
		if (resp != null && resp.trim().length() > 0){
			byte[] bytes = resp.getBytes();
			ByteBuffer writeBuf = ByteBuffer.allocate(bytes.length);
			writeBuf.put(bytes);
			writeBuf.flip();
			channel.write(writeBuf);//sc�Ƿ������ģ������ܱ�֤һ���ܹ����ֽ����鷢���꣬����֡�д���������
		}
	}
}
